Import the required libraries.

library(rayshader)
Warning: package ‘rayshader’ was built under R version 4.4.3Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(terra)
terra 1.8.15
library(rgl)
Warning: package ‘rgl’ was built under R version 4.4.3

Loading DEM

# Download & Load the DEM from GitHub
dem_url <- "https://github.com/Fangsheng-Zhou/rayshader_exploration/raw/refs/heads/main/Example_DEM_Data/USGS_13_n49w122_20230307_mtbaker.tif"
dest_file <- "Data/mtbaker_dem.tif"

# Download and save DEM
download.file(dem_url, destfile = dest_file, mode = "wb")
trying URL 'https://github.com/Fangsheng-Zhou/rayshader_exploration/raw/refs/heads/main/Example_DEM_Data/USGS_13_n49w122_20230307_mtbaker.tif'
Content type 'application/octet-stream' length 20654132 bytes (19.7 MB)
downloaded 19.7 MB
# Read the DEM as a raster
mtbaker_dem <- rast(dest_file)

# Plot the DEM as a grayscale image to verify
plot(mtbaker_dem, main = "Mt Baker DEM")

Downsize DEM for faster rendering

# Downsample DEM by a factor of 3 
mtbaker_dem_downsampled <- aggregate(mtbaker_dem, fact = 3, fun = mean) 

plot(mtbaker_dem_downsampled, main = "Mt Baker Downsampled DEM")

Note: you can compare the before and after DEM using res() or ncell().

Create a 2D plot of the DEM using rayshader.

# Convert to rayshader-compatible matrix
elmat <- raster_to_matrix(mtbaker_dem_downsampled)
[1] "Dimensions of matrix are: 603x863"
# Apply shading and plot the 2D rayshader map
elmat %>%
  sphere_shade(texture = "desert") %>%
  plot_map()

How about adding some shadow?

# Apply texture and simple ray-traced shadows
elmat %>%
  sphere_shade(texture = "desert") %>%
  add_shadow(ray_shade(elmat), 0.5) %>% # Adds realistic lighting effects
  plot_map()

How about plot it in 3D?

# Render in 3D 
elmat %>%
  sphere_shade(texture = "desert") %>%
  add_shadow(ray_shade(elmat), 0.5) %>% # Adds realistic lighting effects
  plot_3d(elmat, zscale = 10, fov = 0, theta = 135, zoom = 0.75, phi = 45, windowsize = c(1000, 800))

# Pause briefly before taking a snapshot
Sys.sleep(3)

# Save a high-quality snapshot
render_snapshot("Data/mtbaker_3D.png")

Add water

elmat %>%
  sphere_shade(texture = "desert") %>%
  add_water(detect_water(elmat, cutoff = 0.3), color = "blue") %>%  # Adjust cutoff
  add_shadow(ray_shade(elmat), 0.5) %>% 
  plot_3d(elmat, zscale = 10, fov = 0, theta = 135, zoom = 0.75, phi = 45, windowsize = c(1000, 800))

2D vs. 3D plots, side by side comparison

library(ggplot2)
library(rayshader)
mtplot = ggplot(mtcars) + 
  geom_point(aes(x = mpg, y = hp, color = cyl)) + 
  scale_color_continuous(limits = c(0, 8))

par(mfrow = c(1, 2))
plot_gg(mtplot, width = 3.5, raytrace = FALSE, preview = TRUE)


plot_gg(mtplot, width = 3.5, multicore = TRUE, windowsize = c(800, 800), 
        zoom = 0.85, phi = 35, theta = 30, sunangle = 225, soliddepth = 0.5)

That’s all for the quick examples!

LS0tDQp0aXRsZTogIkdFT0cgNTk1IHJheXNoYWRlciBFeGFtcGxlcyBieSBKYXNwZXIgWmhvdSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCioqSW1wb3J0IHRoZSByZXF1aXJlZCBsaWJyYXJpZXMuKioNCmBgYHtyfQ0KbGlicmFyeShyYXlzaGFkZXIpDQpsaWJyYXJ5KHRlcnJhKQ0KbGlicmFyeShyZ2wpDQpgYGANCg0KKipMb2FkaW5nIERFTSoqDQpgYGB7cn0NCiMgRG93bmxvYWQgJiBMb2FkIHRoZSBERU0gZnJvbSBHaXRIdWINCmRlbV91cmwgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS9GYW5nc2hlbmctWmhvdS9yYXlzaGFkZXJfZXhwbG9yYXRpb24vcmF3L3JlZnMvaGVhZHMvbWFpbi9FeGFtcGxlX0RFTV9EYXRhL1VTR1NfMTNfbjQ5dzEyMl8yMDIzMDMwN19tdGJha2VyLnRpZiINCmRlc3RfZmlsZSA8LSAiRGF0YS9tdGJha2VyX2RlbS50aWYiDQoNCiMgRG93bmxvYWQgYW5kIHNhdmUgREVNDQpkb3dubG9hZC5maWxlKGRlbV91cmwsIGRlc3RmaWxlID0gZGVzdF9maWxlLCBtb2RlID0gIndiIikNCg0KIyBSZWFkIHRoZSBERU0gYXMgYSByYXN0ZXINCm10YmFrZXJfZGVtIDwtIHJhc3QoZGVzdF9maWxlKQ0KDQojIFBsb3QgdGhlIERFTSBhcyBhIGdyYXlzY2FsZSBpbWFnZSB0byB2ZXJpZnkNCnBsb3QobXRiYWtlcl9kZW0sIG1haW4gPSAiTXQgQmFrZXIgREVNIikNCmBgYA0KDQoqKkRvd25zaXplIERFTSBmb3IgZmFzdGVyIHJlbmRlcmluZyoqDQpgYGB7cn0NCiMgRG93bnNhbXBsZSBERU0gYnkgYSBmYWN0b3Igb2YgMyANCm10YmFrZXJfZGVtX2Rvd25zYW1wbGVkIDwtIGFnZ3JlZ2F0ZShtdGJha2VyX2RlbSwgZmFjdCA9IDMsIGZ1biA9IG1lYW4pIA0KDQpwbG90KG10YmFrZXJfZGVtX2Rvd25zYW1wbGVkLCBtYWluID0gIk10IEJha2VyIERvd25zYW1wbGVkIERFTSIpDQpgYGANCg0KTm90ZTogeW91IGNhbiBjb21wYXJlIHRoZSBiZWZvcmUgYW5kIGFmdGVyIERFTSB1c2luZyByZXMoKSBvciBuY2VsbCgpLg0KDQoqKkNyZWF0ZSBhIDJEIHBsb3Qgb2YgdGhlIERFTSB1c2luZyByYXlzaGFkZXIuKioNCmBgYHtyfQ0KIyBDb252ZXJ0IHRvIHJheXNoYWRlci1jb21wYXRpYmxlIG1hdHJpeA0KZWxtYXQgPC0gcmFzdGVyX3RvX21hdHJpeChtdGJha2VyX2RlbV9kb3duc2FtcGxlZCkNCg0KIyBBcHBseSBzaGFkaW5nIGFuZCBwbG90IHRoZSAyRCByYXlzaGFkZXIgbWFwDQplbG1hdCAlPiUNCiAgc3BoZXJlX3NoYWRlKHRleHR1cmUgPSAiZGVzZXJ0IikgJT4lDQogIHBsb3RfbWFwKCkNCmBgYA0KDQoqKkhvdyBhYm91dCBhZGRpbmcgc29tZSBzaGFkb3c/KioNCmBgYHtyfQ0KIyBBcHBseSB0ZXh0dXJlIGFuZCBzaW1wbGUgcmF5LXRyYWNlZCBzaGFkb3dzDQplbG1hdCAlPiUNCiAgc3BoZXJlX3NoYWRlKHRleHR1cmUgPSAiZGVzZXJ0IikgJT4lDQogIGFkZF9zaGFkb3cocmF5X3NoYWRlKGVsbWF0KSwgMC41KSAlPiUgIyBBZGRzIHJlYWxpc3RpYyBsaWdodGluZyBlZmZlY3RzDQogIHBsb3RfbWFwKCkNCmBgYA0KDQoqKkhvdyBhYm91dCBwbG90IGl0IGluIDNEPyoqDQpgYGB7cn0NCiMgUmVuZGVyIGluIDNEIA0KZWxtYXQgJT4lDQogIHNwaGVyZV9zaGFkZSh0ZXh0dXJlID0gImRlc2VydCIpICU+JQ0KICBhZGRfc2hhZG93KHJheV9zaGFkZShlbG1hdCksIDAuNSkgJT4lICMgQWRkcyByZWFsaXN0aWMgbGlnaHRpbmcgZWZmZWN0cw0KICBwbG90XzNkKGVsbWF0LCB6c2NhbGUgPSAxMCwgZm92ID0gMCwgdGhldGEgPSAxMzUsIHpvb20gPSAwLjc1LCBwaGkgPSA0NSwgd2luZG93c2l6ZSA9IGMoMTAwMCwgODAwKSkNCg0KIyBQYXVzZSBicmllZmx5IGJlZm9yZSB0YWtpbmcgYSBzbmFwc2hvdA0KU3lzLnNsZWVwKDMpDQoNCiMgU2F2ZSBhIGhpZ2gtcXVhbGl0eSBzbmFwc2hvdA0KcmVuZGVyX3NuYXBzaG90KCJEYXRhL210YmFrZXJfM0QucG5nIikNCmBgYA0KDQoqKkFkZCB3YXRlcioqDQpgYGB7cn0NCmVsbWF0ICU+JQ0KICBzcGhlcmVfc2hhZGUodGV4dHVyZSA9ICJkZXNlcnQiKSAlPiUNCiAgYWRkX3dhdGVyKGRldGVjdF93YXRlcihlbG1hdCwgY3V0b2ZmID0gMC4zKSwgY29sb3IgPSAiYmx1ZSIpICU+JSAgIyBBZGp1c3QgY3V0b2ZmDQogIGFkZF9zaGFkb3cocmF5X3NoYWRlKGVsbWF0KSwgMC41KSAlPiUgDQogIHBsb3RfM2QoZWxtYXQsIHpzY2FsZSA9IDEwLCBmb3YgPSAwLCB0aGV0YSA9IDEzNSwgem9vbSA9IDAuNzUsIHBoaSA9IDQ1LCB3aW5kb3dzaXplID0gYygxMDAwLCA4MDApKQ0KYGBgDQoNCioqMkQgdnMuIDNEIHBsb3RzLCBzaWRlIGJ5IHNpZGUgY29tcGFyaXNvbioqDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmF5c2hhZGVyKQ0KYGBgDQoNCmBgYHtyfQ0KbXRwbG90ID0gZ2dwbG90KG10Y2FycykgKyANCiAgZ2VvbV9wb2ludChhZXMoeCA9IG1wZywgeSA9IGhwLCBjb2xvciA9IGN5bCkpICsgDQogIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA4KSkNCg0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnBsb3RfZ2cobXRwbG90LCB3aWR0aCA9IDMuNSwgcmF5dHJhY2UgPSBGQUxTRSwgcHJldmlldyA9IFRSVUUpDQoNCnBsb3RfZ2cobXRwbG90LCB3aWR0aCA9IDMuNSwgbXVsdGljb3JlID0gVFJVRSwgd2luZG93c2l6ZSA9IGMoODAwLCA4MDApLCANCiAgICAgICAgem9vbSA9IDAuODUsIHBoaSA9IDM1LCB0aGV0YSA9IDMwLCBzdW5hbmdsZSA9IDIyNSwgc29saWRkZXB0aCA9IDAuNSkNCmBgYA0KDQoqKlRoYXQncyBhbGwgZm9yIHRoZSBxdWljayBleGFtcGxlcyEqKg==